{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {
    "editable": true,
    "slideshow": {
     "slide_type": ""
    },
    "tags": []
   },
   "source": [
    "# Week 2: Predicting time series\n",
    "\n",
    "Welcome! In the previous assignment you got some exposure to working with time series data, but you didn't use machine learning techniques for your forecasts. This week you will be using a deep neural network to create one step forecasts to see how this technique compares with the ones you already tried out. Once again all of the data is going to be generated.\n",
    "\n",
    "#### TIPS FOR SUCCESSFUL GRADING OF YOUR ASSIGNMENT:\n",
    "\n",
    "- All cells are frozen except for the ones where you need to submit your solutions or when explicitly mentioned you can interact with it.\n",
    "\n",
    "- You can add new cells to experiment but these will be omitted by the grader, so don't rely on newly created cells to host your solution code, use the provided places for this.\n",
    "\n",
    "- You can add the comment # grade-up-to-here in any graded cell to signal the grader that it must only evaluate up to that point. This is helpful if you want to check if you are on the right track even if you are not done with the whole assignment. Be sure to remember to delete the comment afterwards!\n",
    "\n",
    "- Avoid using global variables unless you absolutely have to. The grader tests your code in an isolated environment without running all cells from the top. As a result, global variables may be unavailable when scoring your submission. Global variables that are meant to be used will be defined in UPPERCASE.\n",
    "\n",
    "- To submit your notebook, save it and then click on the blue submit button at the beginning of the page.\n",
    "\n",
    "Let's get started!"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "deletable": false,
    "editable": false,
    "id": "BOjujz601HcS",
    "outputId": "21a00a04-e660-4eb1-dc6f-8ad3741dee5a",
    "tags": [
     "graded"
    ]
   },
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "import tensorflow as tf\n",
    "import matplotlib.pyplot as plt"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "deletable": false,
    "editable": false
   },
   "outputs": [],
   "source": [
    "import unittests"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "editable": true,
    "slideshow": {
     "slide_type": ""
    },
    "tags": []
   },
   "source": [
    "## Generating the data\n",
    "\n",
    "First things first, you will need to generate your time series data. \n",
    "\n",
    "The next cell includes a bunch of helper functions to generate and plot the time series. These are very similar to those you saw on Week 1."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "deletable": false,
    "editable": false,
    "tags": [
     "graded"
    ]
   },
   "outputs": [],
   "source": [
    "def plot_series(time, series, format=\"-\", start=0, end=None):\n",
    "    \"\"\"Plot the series\"\"\"\n",
    "    plt.plot(time[start:end], series[start:end], format)\n",
    "    plt.xlabel(\"Time\")\n",
    "    plt.ylabel(\"Value\")\n",
    "    plt.grid(False)\n",
    "\n",
    "def trend(time, slope=0):\n",
    "    \"\"\"A trend over time\"\"\"\n",
    "    return slope * time\n",
    "\n",
    "def seasonal_pattern(season_time):\n",
    "    \"\"\"Just an arbitrary pattern, you can change it if you wish\"\"\"\n",
    "    return np.where(season_time < 0.1,\n",
    "                    np.cos(season_time * 6 * np.pi), \n",
    "                    2 / np.exp(9 * season_time))\n",
    "\n",
    "def seasonality(time, period, amplitude=1, phase=0):\n",
    "    \"\"\"Repeats the same pattern at each period\"\"\"\n",
    "    season_time = ((time + phase) % period) / period\n",
    "    return amplitude * seasonal_pattern(season_time)\n",
    "\n",
    "def noise(time, noise_level=1, seed=None):\n",
    "    \"\"\"Adds noise to the series\"\"\"\n",
    "    rnd = np.random.RandomState(seed)\n",
    "    return rnd.randn(len(time)) * noise_level"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Now, define a function to generate the time series, using the functions from the previous cell. This function should return a time series that has trend, seasonality and noise. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "deletable": false,
    "editable": false,
    "tags": [
     "graded"
    ]
   },
   "outputs": [],
   "source": [
    "def generate_time_series():\n",
    "    \"\"\" Creates timestamps and values of the time series \"\"\"\n",
    "    \n",
    "    # The time dimension or the x-coordinate of the time series\n",
    "    time = np.arange(4 * 365 + 1, dtype=\"float32\")\n",
    "\n",
    "    # Initial series is just a straight line with a y-intercept\n",
    "    y_intercept = 10\n",
    "    slope = 0.005\n",
    "    series = trend(time, slope) + y_intercept\n",
    "\n",
    "    # Adding seasonality\n",
    "    amplitude = 50\n",
    "    series += seasonality(time, period=365, amplitude=amplitude)\n",
    "\n",
    "    # Adding some noise\n",
    "    noise_level = 3\n",
    "    series += noise(time, noise_level, seed=51)\n",
    "    \n",
    "    return time, series"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Defining some useful global variables\n",
    "\n",
    "Next, you will define some global variables that will be used throughout the assignment. Feel free to reference them in the upcoming exercises:\n",
    "\n",
    "`SPLIT_TIME`: time index to split between train and validation sets\n",
    "\n",
    "`WINDOW_SIZE`: length of the window to use for smoothing the series\n",
    "\n",
    "`BATCH_SIZE`: batch size for training the model\n",
    "\n",
    "`SHUFFLE_BUFFER_SIZE`: number of elements from the dataset used to sample for a new shuffle of the dataset. For more information about the use of this variable you can take a look at the [docs](https://www.tensorflow.org/api_docs/python/tf/data/Dataset#shuffle).\n",
    "\n",
    "**A note about grading:**\n",
    "\n",
    "**When you submit this assignment for grading these same values for these globals will be used so make sure that all your code works well with these values. After submitting and passing this assignment, you are encouraged to come back here and play with these parameters to see the impact they have in the classification process. Since this next cell is frozen, you will need to copy the contents into a new cell and run it to overwrite the values for these globals.**\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "deletable": false,
    "editable": false,
    "slideshow": {
     "slide_type": ""
    },
    "tags": [
     "graded"
    ]
   },
   "outputs": [],
   "source": [
    "# Save all global variables\n",
    "SPLIT_TIME = 1100\n",
    "WINDOW_SIZE = 20\n",
    "BATCH_SIZE = 32\n",
    "SHUFFLE_BUFFER_SIZE = 1000"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "editable": true,
    "slideshow": {
     "slide_type": ""
    },
    "tags": []
   },
   "source": [
    "Finally, put everything together and create the times series you will use for this assignment. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "deletable": false,
    "editable": false,
    "tags": [
     "graded"
    ]
   },
   "outputs": [],
   "source": [
    "# Create the time series\n",
    "TIME, SERIES = generate_time_series()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "deletable": false,
    "editable": false
   },
   "outputs": [],
   "source": [
    "# Plot the generated series\n",
    "plt.figure(figsize=(10, 6))\n",
    "plot_series(TIME, SERIES)\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Splitting the data\n",
    "\n",
    "As usual, you will need a function to split the data between train and validation sets. Since you already coded the `train_val_split` function during last week's assignment, this time it is provided for you:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 645
    },
    "deletable": false,
    "editable": false,
    "id": "Zswl7jRtGzkk",
    "outputId": "acfe857a-4a0f-4d41-bd45-1df7fa26f4d0",
    "tags": [
     "graded"
    ]
   },
   "outputs": [],
   "source": [
    "def train_val_split(time, series):\n",
    "\n",
    "    time_train = time[:SPLIT_TIME]\n",
    "    series_train = series[:SPLIT_TIME]\n",
    "    time_valid = time[SPLIT_TIME:]\n",
    "    series_valid = series[SPLIT_TIME:]\n",
    "\n",
    "    return time_train, series_train, time_valid, series_valid\n",
    "\n",
    "\n",
    "# Split the dataset\n",
    "time_train, series_train, time_valid, series_valid = train_val_split(TIME, SERIES)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "editable": true,
    "slideshow": {
     "slide_type": ""
    },
    "tags": []
   },
   "source": [
    "## Processing the data\n",
    "\n",
    "### Exercise 1: windowed_dataset\n",
    "\n",
    "As you saw on the lectures, you can feed the data for training by creating a TF Dataset with the appropriate processing steps such as `windowing`, `flattening`, `batching` and `shuffling`. Remember you can do all these using the different methods of the `tf.data.Dataset` object. Next, complete the `windowed_dataset` function below that effectively pre-processes your time series and returns a TF Dataset.\n",
    "\n",
    "This function receives a `series` and a `window_size`, and returns a TF Dataset. You should already be familiar with `tf.data.Dataset` objects from the this week's lectures, but be sure to check out the [docs](https://www.tensorflow.org/api_docs/python/tf/data/Dataset) if you need any help."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "deletable": false,
    "editable": true,
    "id": "4sTTIOCbyShY",
    "slideshow": {
     "slide_type": ""
    },
    "tags": [
     "graded"
    ]
   },
   "outputs": [],
   "source": [
    "# GRADED FUNCTION: windowed_dataset\n",
    "def windowed_dataset(series, window_size, shuffle=True):\n",
    "    \"\"\"Create a windowed dataset\n",
    "\n",
    "    Args:\n",
    "        series (np.ndarray): time series\n",
    "        window_size (int): length of window to use for prediction\n",
    "        shuffle (bool): (For testing purposes) Indicates whether to shuffle data before batching or not. Defaults to True\n",
    "\n",
    "    Returns:\n",
    "        td.data.Dataset: windowed dataset\n",
    "    \"\"\"\n",
    "    \n",
    "    ### START CODE HERE ###\n",
    "    # Create dataset from the series. \n",
    "    # HINT: use an appropriate method from the tf.data.Dataset object\n",
    "    dataset = None\n",
    "    \n",
    "    # Slice the dataset into the appropriate windows\n",
    "    dataset = None\n",
    "    \n",
    "    # Flatten the dataset\n",
    "    dataset = None\n",
    "    \n",
    "    # Shuffle it\n",
    "    if shuffle:  # For testing purposes\n",
    "        dataset = None \n",
    "    \n",
    "    # Split it into the features and labels. \n",
    "    dataset = None\n",
    "    \n",
    "    # Batch it\n",
    "    dataset = None\n",
    "    \n",
    "    ### END CODE HERE ###\n",
    "    \n",
    "    return dataset"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "editable": true,
    "slideshow": {
     "slide_type": ""
    },
    "tags": []
   },
   "source": [
    "To test your function you will be using a `window_size` of 10 which means that you will use 10 consecutive values to predict the next one. You will also set the parameter `shuffle=False`. Given this, the first element of the batch of features should be identical to the first 15 elements of the `series_train`, and the batch of labels should be equal to elements 10 through 42 of the `series_train`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "deletable": false,
    "editable": false,
    "slideshow": {
     "slide_type": ""
    },
    "tags": []
   },
   "outputs": [],
   "source": [
    "# Try out your function with windows size of 1 and no shuffling\n",
    "test_dataset = windowed_dataset(series_train, window_size=10,shuffle= False)\n",
    "\n",
    "# Get the first batch of the test dataset\n",
    "batch_of_features, batch_of_labels = next((iter(test_dataset)))\n",
    "\n",
    "print(f\"batch_of_features has type: {type(batch_of_features)}\\n\")\n",
    "print(f\"batch_of_labels has type: {type(batch_of_labels)}\\n\")\n",
    "print(f\"batch_of_features has shape: {batch_of_features.shape}\\n\")\n",
    "print(f\"batch_of_labels has shape: {batch_of_labels.shape}\\n\")\n",
    "print(f\"First element in batch_of_features is equal to first 10 elements in the series: {np.allclose(batch_of_features.numpy()[0].flatten(), series_train[:10])}\\n\")\n",
    "print(f\"batch_of_labels is equal to the first 32 values after the window_lenght of 10): {np.allclose(batch_of_labels.numpy(), series_train[10:BATCH_SIZE+10])}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "editable": true,
    "slideshow": {
     "slide_type": ""
    },
    "tags": []
   },
   "source": [
    "**Expected Output:**\n",
    "\n",
    "```\n",
    "batch_of_features has type: <class 'tensorflow.python.framework.ops.EagerTensor'>\n",
    "\n",
    "batch_of_labels has type: <class 'tensorflow.python.framework.ops.EagerTensor'>\n",
    "\n",
    "batch_of_features has shape: (32, 10)\n",
    "\n",
    "batch_of_labels has shape: (32,)\n",
    "\n",
    "First element in batch_of_features is equal to first 10 elements in the series: True\n",
    "\n",
    "batch_of_labels is equal to the first 32 values after the window_lenght of 10): True\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "editable": true,
    "slideshow": {
     "slide_type": ""
    },
    "tags": []
   },
   "source": [
    "Now plot the first item in the batch. You will be displayng the 20 features, followed by the label, which is the value you want to predict. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "deletable": false,
    "editable": false,
    "slideshow": {
     "slide_type": ""
    },
    "tags": []
   },
   "outputs": [],
   "source": [
    "plt.plot(np.arange(10), batch_of_features[0].numpy(), label='features')\n",
    "plt.plot(np.arange(9,11), [batch_of_features[0].numpy()[-1], batch_of_labels[0].numpy()], label='label');\n",
    "plt.legend()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Now that you have tested your `windowed_dataset` function, use it to create your train dataset. For that, just run the cell below"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "deletable": false,
    "editable": false,
    "tags": []
   },
   "outputs": [],
   "source": [
    "# Apply the processing to the whole training series\n",
    "train_dataset = windowed_dataset(series_train, WINDOW_SIZE)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "deletable": false,
    "editable": false
   },
   "outputs": [],
   "source": [
    "# Test your code!\n",
    "unittests.test_windowed_dataset(windowed_dataset)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "editable": true,
    "slideshow": {
     "slide_type": ""
    },
    "tags": []
   },
   "source": [
    "## Defining the model architecture\n",
    "\n",
    "### Exercise 2: create_model \n",
    "\n",
    "Now that you have a function that will process the data before it is fed into your neural network for training, it is time to define you model architecture.\n",
    "\n",
    "Complete the `create_model` function below. Notice that this function receives the `window_size` since this will be an important parameter for the first layer of your network.\n",
    "\n",
    "Remember that this time you are predicting the values of a time series, so use an appropriate loss for this task. There are many you can choose for, but for grading purposes, please stick to 'mse'. \n",
    "\n",
    "Hint:\n",
    "- You will only need `Dense` layers.\n",
    "- The training should be really quick so if you notice that each epoch is taking more than a few seconds, consider trying a different architecture.\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "deletable": false,
    "editable": true,
    "id": "TW-vT7eLYAdb",
    "outputId": "94611183-4107-4062-cefd-c79d902d4e2f",
    "slideshow": {
     "slide_type": ""
    },
    "tags": [
     "graded"
    ]
   },
   "outputs": [],
   "source": [
    "# GRADED FUNCTION: create_model\n",
    "def create_model(window_size):\n",
    "    \"\"\"Create model for predictions\n",
    "    Args:\n",
    "        window_size (int): length of window to use for prediction\n",
    "\n",
    "    Returns:\n",
    "        tf.keras.Model: model\n",
    "    \"\"\"\n",
    "    ### START CODE HERE ###\n",
    "\n",
    "    model = tf.keras.models.Sequential([ \n",
    "        tf.keras.Input(shape=None),\n",
    "        \n",
    "    ]) \n",
    "\n",
    "    model.compile(loss=None,\n",
    "                  optimizer=None)\n",
    "    \n",
    "    ### END CODE HERE ###\n",
    "\n",
    "    return model"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The next cell allows you to check the number of total and trainable parameters of your model and prompts a warning in case these exceeds those of a reference solution, this serves the following 3 purposes listed in order of priority:\n",
    "\n",
    "- Helps you prevent crashing the kernel during training.\n",
    "\n",
    "- Helps you avoid longer-than-necessary training times.\n",
    "\n",
    "- Provides a reasonable estimate of the size of your model. In general you will usually prefer smaller models given that they accomplish their goal successfully.\n",
    "\n",
    "**Notice that this is just informative** and may be very well below the actual limit for size of the model necessary to crash the kernel. So even if you exceed this reference you are probably fine. However, **if the kernel crashes during training or it is taking a very long time and your model is larger than the reference, come back here and try to get the number of parameters closer to the reference.**"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "deletable": false,
    "editable": false,
    "scrollable": true,
    "scrolled": true,
    "slideshow": {
     "slide_type": ""
    },
    "tags": []
   },
   "outputs": [],
   "source": [
    "# Get the untrained model\n",
    "model = create_model(WINDOW_SIZE)\n",
    "\n",
    "# Check the parameter count against a reference solution\n",
    "unittests.parameter_count(model)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "deletable": false,
    "editable": false
   },
   "outputs": [],
   "source": [
    "example_batch = train_dataset.take(1)\n",
    "\n",
    "try:\n",
    "\tmodel.evaluate(example_batch, verbose=False)\n",
    "except:\n",
    "\tprint(\"Your model is not compatible with the dataset you defined earlier. Check that the loss function and last layer are compatible with one another.\")\n",
    "else:\n",
    "\tpredictions = model.predict(example_batch, verbose=False)\n",
    "\tprint(f\"predictions have shape: {predictions.shape}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Expected output:**\n",
    "```\n",
    "predictions have shape: (NUM_BATCHES, 1)\n",
    "\n",
    "Where NUM_BATCHES is the number of batches you have set to your dataset.\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Before going any further, check that the input and output dimensions of your model are correct. Do this by running the cell below:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "deletable": false,
    "editable": false,
    "tags": []
   },
   "outputs": [],
   "source": [
    "print(f'Model input shape: {model.input_shape}')\n",
    "print(f'Model output shape: {model.output_shape}')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "You can also print a summary of your model to see what the architecture looks like. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "deletable": false,
    "editable": false,
    "tags": []
   },
   "outputs": [],
   "source": [
    "model.summary()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "deletable": false,
    "editable": false
   },
   "outputs": [],
   "source": [
    "# Test your code!\n",
    "unittests.test_create_model(create_model, windowed_dataset)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "deletable": false,
    "editable": false,
    "scrollable": true,
    "scrolled": true,
    "slideshow": {
     "slide_type": ""
    },
    "tags": []
   },
   "outputs": [],
   "source": [
    "# Train it\n",
    "history = model.fit(train_dataset, epochs=100)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "editable": true,
    "slideshow": {
     "slide_type": ""
    },
    "tags": []
   },
   "source": [
    "Now go ahead and plot the training loss so you can monitor the learning process."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "deletable": false,
    "editable": false,
    "slideshow": {
     "slide_type": ""
    },
    "tags": []
   },
   "outputs": [],
   "source": [
    "# Plot the training loss for each epoch\n",
    "\n",
    "loss = history.history['loss']\n",
    "\n",
    "epochs = range(len(loss))\n",
    "\n",
    "plt.plot(epochs, loss, 'r', label='Training loss')\n",
    "plt.title('Training loss')\n",
    "plt.legend(loc=0)\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "editable": true,
    "slideshow": {
     "slide_type": ""
    },
    "tags": []
   },
   "source": [
    "## Evaluating the forecast\n",
    "\n",
    "Now it is time to evaluate the performance of the forecast. For this you can use the `compute_metrics` function that you coded in the previous assignment:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "deletable": false,
    "editable": false,
    "slideshow": {
     "slide_type": ""
    },
    "tags": [
     "graded"
    ]
   },
   "outputs": [],
   "source": [
    "def compute_metrics(true_series, forecast):\n",
    "    mse = tf.keras.losses.MSE(true_series, forecast)\n",
    "    mae = tf.keras.losses.MAE(true_series, forecast)\n",
    "    return mse, mae"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "You will also be generating `predict_forecast` function, that simply computes predictions for all values in the validation data. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 388
    },
    "deletable": false,
    "editable": false,
    "id": "efhco2rYyIFF",
    "outputId": "3ee47e36-7681-4d6b-9c9c-ad73883c3fda",
    "slideshow": {
     "slide_type": ""
    },
    "tags": [
     "graded"
    ]
   },
   "outputs": [],
   "source": [
    "def generate_forecast(model, series, window_size):\n",
    "    forecast = []\n",
    "    for time in range(SPLIT_TIME, len(series)):\n",
    "        pred = model.predict(series[time-window_size:time][np.newaxis], verbose=0)\n",
    "        forecast.append(pred[0][0])\n",
    "    return forecast"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Now, go ahead and make the predictions. This run should take no more time than the actual training.  "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 388
    },
    "deletable": false,
    "editable": false,
    "id": "efhco2rYyIFF",
    "outputId": "3ee47e36-7681-4d6b-9c9c-ad73883c3fda",
    "scrolled": true,
    "slideshow": {
     "slide_type": ""
    },
    "tags": []
   },
   "outputs": [],
   "source": [
    "# Save the forecast\n",
    "dnn_forecast = generate_forecast(model, SERIES, WINDOW_SIZE)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "You can now plot the true series, and the predicted series in order to get a visual estimate of how good your model is doing."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 388
    },
    "deletable": false,
    "editable": false,
    "id": "efhco2rYyIFF",
    "outputId": "3ee47e36-7681-4d6b-9c9c-ad73883c3fda",
    "scrolled": true,
    "slideshow": {
     "slide_type": ""
    },
    "tags": []
   },
   "outputs": [],
   "source": [
    "# Plot it\n",
    "plt.figure(figsize=(10, 4))\n",
    "plot_series(time_valid, series_valid)\n",
    "plot_series(time_valid, dnn_forecast)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "editable": true,
    "slideshow": {
     "slide_type": ""
    },
    "tags": []
   },
   "source": [
    "**Expected Output:**\n",
    "\n",
    "A series similar to this one:\n",
    "\n",
    "<div>\n",
    "<img src=\"images/forecast.png\" width=\"800\">\n",
    "</div>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Finally, go ahead and compute the MSE and MAE metrics using the `compute_metrics` function you defined earlier."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "deletable": false,
    "editable": false,
    "slideshow": {
     "slide_type": ""
    },
    "tags": []
   },
   "outputs": [],
   "source": [
    "mse, mae = compute_metrics(series_valid, dnn_forecast)\n",
    "\n",
    "print(f\"mse: {mse:.2f}, mae: {mae:.2f} for forecast\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "editable": true,
    "slideshow": {
     "slide_type": ""
    },
    "tags": []
   },
   "source": [
    "**To pass this assignment your forecast should achieve an MSE of 30 or less.**\n",
    "\n",
    "- If your forecast didn't achieve this threshold try re-training your model with a different architecture or tweaking the optimizer's parameters.\n",
    "\n",
    "\n",
    "- If your forecast did achieve this threshold run the following cell to save the MSE in a binary file which will be used for grading and after doing so, submit your assigment for grading."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "deletable": false,
    "editable": false,
    "slideshow": {
     "slide_type": ""
    },
    "tags": []
   },
   "outputs": [],
   "source": [
    "# ONLY RUN THIS CELL IF YOUR MSE ACHIEVED THE DESIRED MSE LEVEL\n",
    "# Save your model \n",
    "model.save('trained_model.keras')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "editable": true,
    "slideshow": {
     "slide_type": ""
    },
    "tags": []
   },
   "source": [
    "**Congratulations on finishing this week's assignment!**\n",
    "\n",
    "You have successfully implemented a neural network capable of forecasting time series while also learning how to leverage Tensorflow's Dataset class to process time series data!\n",
    "\n",
    "**Keep it up!**"
   ]
  }
 ],
 "metadata": {
  "colab": {
   "collapsed_sections": [],
   "name": "C4_W2_Assignment_Solution.ipynb",
   "provenance": []
  },
  "grader_version": "1",
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.11.6"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
